/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2021-2023. All rights reserved.
 *
 */

import { FileSet, Json5Reader, MetricLogType, TaskInputValue } from '@ohos/hvigor';
import { ModuleInfoType, ProjectConfig } from '@ohos/hvigor-arkts-compose';
import fs from 'fs';
import path from 'path';

import { ArkPackConst, CommonConst } from '../../const/common-const.js';
import { CodeType } from '../../enum/code-type-enum.js';
import { OhosLogger } from '../../utils/log/ohos-logger.js';
import { limitMinApiVersion } from '../../utils/task-util.js';
import { AbstractCompileNode } from '../abstract/abstract-compile-node.js';
import { TaskNames } from '../common/task-names.js';
import { LegacyPreBuild } from '../legacy-tasks/legacy-pre-build.js';
import { LegacyProcessProfile } from '../legacy-tasks/legacy-process-profile.js';
import { PreBuild } from '../pre-build.js';
import { TargetTaskService } from '../service/target-task-service.js';
import { PackageJsonOpt } from '../task/ohos-har-task.js';
import { submitArkLintWork } from '../worker/run-ark.js';
import CommonTask = TaskNames.CommonTask;
import Task = TaskNames.Task;
import * as fse from 'fs-extra';

import { ApiVersion } from '../../const/sdk-const.js';

/**
 * 开源library arkTS代码检查
 */
export class ArkLinter extends AbstractCompileNode {
  protected _log: OhosLogger = OhosLogger.getLogger(ArkLinter.name);

  constructor(taskService: TargetTaskService) {
    super(taskService, CodeType.ETS, Task.LINT);
  }

  taskShouldDo(): boolean {
    return super.taskShouldDo() && this.codeType === CodeType.ETS && this.sdkInfo.hasRollUpPluginInEtsLoader;
  }

  protected async doTaskAction(): Promise<void> {
    const subTaskName = 'initialize and add necessary parameters';
    const subDurationEvent = this.durationEvent.createSubEvent(subTaskName, '');
    subDurationEvent.start();

    // 初始化入口信息, 默认oh-package.json5/main的入口文件
    const indexPath: string = await this.findAvailableEntry();

    // 初始化projectConfig并补充必要参数
    const projectConfig: ProjectConfig = await this.customizeProjectConfig();
    const relative: string = path.relative(projectConfig.aceModuleRoot, indexPath);
    const entry: Record<string, string> = {};
    entry[relative] = indexPath;
    this.service.getDependencyMainPaths();

    subDurationEvent.stop();
    subDurationEvent.setLog(subTaskName, MetricLogType.INFO);

    const subToolTaskName = 'submit ark linter task to work pool';
    const subToolDurationEvent = this.durationEvent.createSubEvent(subToolTaskName, '');
    subToolDurationEvent.start();

    // 执行arkTS代码检查
    await submitArkLintWork(this, this.getWorkerPool(), this._log, projectConfig, entry, subToolDurationEvent);

    subToolDurationEvent.stop();
    subToolDurationEvent.setLog(subToolTaskName, MetricLogType.INFO);
  }

  /**
   * 检查是否存在oh-package.json5中的main字段对应的入口文件, eg. index.ets
   */
  async findAvailableEntry() {
    const packageJson: PackageJsonOpt = await Json5Reader.readJson5File(this.packageManagerPath);
    const moduleDir: string = this.moduleModel.getProjectDir();
    if (!packageJson.main) {
      const availableEntry = ['ets', 'ts', 'js'].find((type) => fs.existsSync(path.resolve(moduleDir, `index.${type}`)));
      if (!availableEntry) {
        this._log
          ._buildError('No available main file defined in oh-package.json.')
          ._file(this.moduleModel.getOhPackageJson5Path())
          ._printErrorAndExit(this.moduleName);
      }
      return path.resolve(moduleDir, `index.${availableEntry}`);
    }
    if (packageJson.main && !fs.existsSync(path.resolve(moduleDir, packageJson.main))) {
      this._log
        ._buildError(`Invalid main file ${packageJson.main} defined in oh-package.json.`)
        ._file(this.moduleModel.getOhPackageJson5Path())
        ._printErrorAndExit(this.moduleName);
    }
    return path.resolve(moduleDir, packageJson.main);
  }

  async customizeProjectConfig() {
    const projectConfig: ProjectConfig = await this.initDefaultArkCompileConfig();
    process.env.compileTool = ArkPackConst.COMPILE_TOOL;
    projectConfig.projectPath = projectConfig.projectTopDir;
    const modules: Record<string, ModuleInfoType> = {};
    this.projectModel
      .getSubModuleModels()
      .forEach((model, name) => (modules[name] = { belongProjectPath: model.getProjectDir(), moduleName: name, modulePkgPath: model.getProjectDir() }));
    projectConfig.projectModel = modules;
    return projectConfig;
  }

  initTaskDepends(): void {
    this.declareDepends(this.isFaMode ? LegacyPreBuild.name : PreBuild.name);
    this.declareDepends(LegacyProcessProfile.name);
    if (limitMinApiVersion(this.targetData, ApiVersion.API_VERSION_9) && !this.isFaMode) {
      this.declareDepends(CommonTask.CREATE_HAR_BUILD_PROFILE.name);
    }
  }

  /**
   * 新增arkLinter对buildMode改变的增量判断
   */
  declareInputs(): Map<string, TaskInputValue> {
    return new Map().set('customTypes', this.targetService.getCustomTypes() ?? '').set(CommonConst.BUILD_MODE, this.targetService.getBuildMode());
  }

  declareInputFiles(): FileSet {
    const fileSet: FileSet = new FileSet();
    if (this.sourcePath && fs.existsSync(this.sourcePath)) {
      fileSet.addEntry(this.sourcePath, { isDirectory: true });
    }

    // 获取当前模块的buildProfile.ets文件 如果存在添加增量判断
    const harBuildProfilePath = this.pathInfo.getHarGenerateBuildProfilePath();
    if (fse.existsSync(harBuildProfilePath)) {
      fileSet.addEntry(harBuildProfilePath);
    }
    return fileSet;
  }

  /**
   * 继承的AbstractCompileNode存在declareOutputFiles,会触发增量判断导致该任务增量失效
   */
  declareOutputFiles(): FileSet {
    return new FileSet();
  }
}
